package com.cmc.cloud.cmclink.doc.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmc.cloud.cmclink.doc.api.document.emums.HsCodeDataSourceEnum;
import com.cmc.cloud.cmclink.doc.api.document.rep.HsCodeRespDto;
import com.cmc.cloud.cmclink.doc.api.document.req.HsCodeReqDto;
import com.cmc.cloud.cmclink.doc.constants.PromptConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.HsCodeErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.HsCodeConvert;
import com.cmc.cloud.cmclink.doc.entity.HsCodeDO;
import com.cmc.cloud.cmclink.doc.mapper.HsCodeMapper;
import com.cmc.cloud.cmclink.doc.service.HsCodeService;
import com.cmc.cloud.cmclink.doc.vo.hscodevo.*;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


/**
 * hscode Service 实现类
 *
 * @author 招商轮船-管理员
 */
@Service
@Validated
public class HsCodeServiceImpl implements HsCodeService {

    @Resource
    private HsCodeMapper hsCodeMapper;

    private static final Pattern PATTERN = Pattern.compile("^\\d{2,10}$");

    @Override
    @Lock4j(name = "hsCodeUnique", keys = {"#createReqVO.getHsCode"})
    public void createHsCode(HsCodeCreateReqVO createReqVO) {
        // 插入
        validateUniqueExists(createReqVO, null);
        if (StringUtils.isEmpty(createReqVO.getDescCn())) {
            createReqVO.setDescCn(createReqVO.getDescEn());
        }
        HsCodeDO hsCode = BeanUtil.copyProperties(createReqVO, HsCodeDO.class);
        hsCode.setDataSource(HsCodeDataSourceEnum.MANUAL.name());
        hsCodeMapper.insert(hsCode);
    }

    @Override
    @Lock4j(name = "hsCodeUnique", keys = {"#updateReqVO.getHsCode"})
    public void updateHsCode(HsCodeUpdateReqVO updateReqVO) {
        // 校验存在
        validateHsCodeExists(updateReqVO.getId());
        validateUniqueExists(updateReqVO, updateReqVO.getId());
        if (StringUtils.isEmpty(updateReqVO.getDescCn())) {
            updateReqVO.setDescCn(updateReqVO.getDescEn());
        }
        if (HsCodeDataSourceEnum.IMPORT.name().equals(updateReqVO.getDataSource())) {
            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.EXCEL_IMPORT_CANNOT_UPDATE);
        }
        // 更新
        HsCodeDO updateObj = BeanUtil.copyProperties(updateReqVO, HsCodeDO.class);
        hsCodeMapper.updateById(updateObj);
    }

    @Override
    public void deleteHsCode(Long id) {
        // 校验存在
        HsCodeDO hsCodeDO = hsCodeMapper.selectById(id);
        if (hsCodeDO == null) {
            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.HS_CODE_NOT_EXISTS);
        }
        if (HsCodeDataSourceEnum.IMPORT.name().equals(hsCodeDO.getDataSource())) {
            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.EXCEL_IMPORT_CANNOT_DELETE);
        }
        // 删除
        hsCodeMapper.deleteById(id);
    }

    @Override
    public void deleteHsCode() {
        // 删除
        hsCodeMapper.delete(new LambdaQueryWrapperX<HsCodeDO>().eq(HsCodeDO::getDataSource, HsCodeDataSourceEnum.IMPORT.name()));
    }

    private void validateHsCodeExists(Long id) {
        if (hsCodeMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.HS_CODE_NOT_EXISTS);
        }
    }

    private void validateUniqueExists(HsCodeBaseVO vo, Long id) {
        LambdaQueryWrapperX<HsCodeDO> queryWrapperX = new LambdaQueryWrapperX<HsCodeDO>()
                .eqIfPresent(HsCodeDO::getHsCode, vo.getHsCode());
        if (id != null) {
            queryWrapperX.ne(HsCodeDO::getId, id);
        }
        if (hsCodeMapper.selectCount(queryWrapperX) != 0L) {
            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.HS_CODE_REPEAT);
        }
    }

    @Override
    public HsCodeDO getHsCode(Long id) {
        return hsCodeMapper.selectById(id);
    }

    @Override
    public List<HsCodeDO> getHsCodeList(Collection<Long> ids) {
        return hsCodeMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<HsCodeDO> getHsCodePage(HsCodePageReqVO reqVO) {


        LambdaQueryWrapper<HsCodeDO> queryWrapperX = new LambdaQueryWrapperX<HsCodeDO>()
                .eqIfPresent(HsCodeDO::getDataSource, reqVO.getDataSource())
                .eq(HsCodeDO::getEnable,true)
                .and(!(ObjectUtils.isEmpty(reqVO.getHsCode()) && ObjectUtils.isEmpty(reqVO.getDescCn()) && ObjectUtils.isEmpty(reqVO.getDescEn())),new Consumer<LambdaQueryWrapper<HsCodeDO>>() {
                    @Override
                    public void accept(LambdaQueryWrapper<HsCodeDO> hsCodeDoLambdaQueryWrapper) {
                        hsCodeDoLambdaQueryWrapper.or().like(!ObjectUtils.isEmpty(reqVO.getHsCode()),HsCodeDO::getHsCode, reqVO.getHsCode());
                        hsCodeDoLambdaQueryWrapper.or().like(!ObjectUtils.isEmpty(reqVO.getDescCn()),HsCodeDO::getDescCn, reqVO.getDescCn());
                        hsCodeDoLambdaQueryWrapper.or().like(!ObjectUtils.isEmpty(reqVO.getDescEn()),HsCodeDO::getDescEn, reqVO.getDescEn());
                    }
                })
                ;
        return hsCodeMapper.selectPage(reqVO, queryWrapperX);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveImportData(List<HsCodeImportVo> exportVoList) {
        //将集合分成2000一组 去保存
        int batchSize = 2000;
        List<List<HsCodeImportVo>> partitions = IntStream.range(0, (exportVoList.size() + batchSize - 1) / batchSize)
                .mapToObj(i -> exportVoList.subList(i * batchSize, Math.min(batchSize * (i + 1), exportVoList.size())))
                .collect(Collectors.toList());

        partitions.forEach(partition -> {
                    // 处理每个子集合
                    if (partition.stream().filter(vo -> StringUtils.isEmpty(vo.getHsCode()) || StringUtils.isEmpty(vo.getDescEn())).findAny().isPresent()) {
                        throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.EXCEL_IMPORT_CODE_NOT_NULL);
                    }
                    partition.forEach(entity -> {
                entity.setHsCode(entity.getHsCode().replaceAll("\\s+", ""));
                entity.setDescEn(entity.getDescEn().replaceAll("-", ""));
                        if (!PATTERN.matcher(entity.getHsCode()).matches()) {
                            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.EXCEL_IMPORT_CODE_OVER_LENGTH, entity.getRowNumber());
                        }
                        if (entity.getDescEn().length() > PromptConstants.EN_LENGTH) {
                            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.EXCEL_IMPORT_EN_OVER_LENGTH, entity.getRowNumber(), PromptConstants.EN_LENGTH);
                        }
                        if (StringUtils.isNotEmpty(entity.getDescCn()) && entity.getDescCn().length() > PromptConstants.CN_LENGTH) {
                            throw ServiceExceptionUtil.exception(HsCodeErrorCodeConstants.EXCEL_IMPORT_CN_OVER_LENGTH, entity.getRowNumber(), PromptConstants.CN_LENGTH);
                        }
                        entity.setDataSource(HsCodeDataSourceEnum.IMPORT.name());
                        if (StringUtils.isEmpty(entity.getDescCn())) {
                            entity.setDescCn(entity.getDescEn());
                        }
                    });
                    List<String> codeList = partition.stream().map(vo -> vo.getHsCode()).collect(Collectors.toList());
                    LambdaQueryWrapperX<HsCodeDO> queryWrapperX = new LambdaQueryWrapperX<>();
                    queryWrapperX.in(HsCodeDO::getHsCode, codeList);
                    //需要修改的的数据
                    List<HsCodeDO> hsCodeDos = hsCodeMapper.selectList(queryWrapperX);

                    //如果hscode在库中存在，则覆盖
                    if (CollectionUtil.isNotEmpty(hsCodeDos)) {
                        Map<String, Long> map = hsCodeDos.stream().collect(Collectors.toMap(HsCodeDO::getHsCode, HsCodeDO::getId));
                        List<String> existedList = hsCodeDos.stream().map(vo -> vo.getHsCode()).collect(Collectors.toList());
                        partition.forEach(entity -> {
                            if (existedList.contains(entity.getHsCode())) {
                                entity.setId(map.get(entity.getHsCode()));
                            }
                        });
                    }
                    List<HsCodeDO> hsCodeDoList = BeanUtil.copyToList(partition, HsCodeDO.class);
                    hsCodeMapper.saveOrUpdateBatch(hsCodeDoList);
                }
        );
        return exportVoList.size();
    }

    @Override
    public List<HsCodeExportVo> getExportList() {
        List<HsCodeDO> hsCodeDos = hsCodeMapper.selectList(new LambdaQueryWrapperX<HsCodeDO>().eq(HsCodeDO::getDataSource, HsCodeDataSourceEnum.IMPORT.name()));
        List<HsCodeExportVo> hsCodeExportVos = BeanUtil.copyToList(hsCodeDos, HsCodeExportVo.class);
        return hsCodeExportVos;
    }

    @Override
    public List<HsCodeRespDto> queryHsCodeInfo(HsCodeReqDto hsCodeReqDto) {
        LambdaQueryWrapperX<HsCodeDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eq(HsCodeDO::getEnable, true);
        if (StringUtils.isNotEmpty(hsCodeReqDto.getKeyWord())) {
            lambdaQueryWrapperX.and(wq -> wq
                    .like(HsCodeDO::getHsCode, hsCodeReqDto.getKeyWord()).or()
                    .like(HsCodeDO::getDescEn, hsCodeReqDto.getKeyWord()).or()
                    .like(HsCodeDO::getDescCn, hsCodeReqDto.getKeyWord())
            );
        }
        List<HsCodeDO> hsCodeDoList = hsCodeMapper.selectList(lambdaQueryWrapperX);
        if (CollectionUtils.isEmpty(hsCodeDoList)) {
            return Collections.emptyList();
        }
        return HsCodeConvert.INSTANCE.convertHsCode(hsCodeDoList);
    }
}
